﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;

namespace Neitzel.Irc.Client
{
    /// <summary>
    /// Stores all Messages to a Channel
    /// </summary>
    public class IrcChannel : MessageStore, INotifyPropertyChanged
    {
        #region Fields

        /// <summary>
        /// Log4net logger to use inside this class.
        /// </summary>
        private static readonly TraceSource Logger = new TraceSource(IrcConstants.TraceSourceName);

        /// <summary>
        /// IrcClient the channel is connected to.
        /// </summary>
        private readonly IrcClient _ircClient;

        private readonly Dictionary<string, ChannelUser> _users;

        #endregion

        #region Lifetime

        /// <summary>
        /// Initializes a new instance of the IrcChannel class.
        /// </summary>
        /// <param name="client">IrcClient the channel is connected to.</param>
        /// <param name="channelName">Name of Channel that we store messages for.</param>
        public IrcChannel(IrcClient client, string channelName) : base(channelName)
        {
            _ircClient = client;
            _users = new Dictionary<string, ChannelUser>();
            MaxNumberOfMessages = Properties.Settings.Default.MaxNumberOfMessagesChannel;
        }

        #endregion

        #region Events

        /// <summary>
        /// Event signaling that a user joined the channel.
        /// </summary>
        public event EventHandler<ChannelUserEventArgs> UserJoined;

        /// <summary>
        /// Event signaling that a user left the channel.
        /// </summary>
        public event EventHandler<ChannelUserEventArgs> UserLeft;

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets a value that indicates whether the user is still in the channel or not.
        /// </summary>
        public bool InChannel
        {
            get { return _inChannel; }
            set { _inChannel = value; OnPropertyChanged(this, new PropertyChangedEventArgs(nameof(InChannel))); }
        }
        private bool _inChannel;

        #endregion

        #region Protected Methods

        /// <summary>
        /// Send the UserJoined event.
        /// </summary>
        /// <param name="e">Event argument.</param>
        protected virtual void OnUserJoined(ChannelUserEventArgs e)
        {
            // Local copy to prevent race conditions
            var handler = UserJoined;

            // raise the event
            handler?.Invoke(this, e);
        }

        /// <summary>
        /// Send the UserLeft event.
        /// </summary>
        /// <param name="e">Event argument.</param>
        protected virtual void OnUserLeft(ChannelUserEventArgs e)
        {
            // Local copy to prevent race conditions
            var handler = UserLeft;

            // raise the event
            handler?.Invoke(this, e);
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Sends a message to the client.
        /// </summary>
        /// <param name="message">Message to send to the client.</param>
        public void SendMessage(string message)
        {
            if (!string.IsNullOrWhiteSpace(TargetName))
                _ircClient.Send(String.Format(CultureInfo.InvariantCulture, IrcMessage.PrivmsgMessage, TargetName, message));
        }

        /// <summary>
        /// Add a user to the channel.
        /// </summary>
        /// <param name="userName">Name of new user.</param>
        public void AddUser(string userName)
        {
            _users.Add(userName, new ChannelUser(userName));
            OnUserJoined(new ChannelUserEventArgs()
                             {
                                 Channel = this,
                                 Joined = true,
                                 Nickname = userName
                             });
        }

        /// <summary>
        /// Remove a user from channel.
        /// </summary>
        /// <param name="userName">Name of user to remove.</param>
        public void RemoveUser(string userName)
        {
            _users.Remove(userName);
            OnUserLeft(new ChannelUserEventArgs()
                           {
                               Channel = this,
                               Left = true,
                               Nickname = userName
                           });
        }

        #endregion

        #region INotifyPropertyChanged implementation

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            // prevent racing conditions
            var handler = PropertyChanged;

            // trigger event
            handler?.Invoke(sender, e);
        }

        #endregion
    }
}